Quiz (Week 7)
Table of Contents
1 Applicatives
1.1 Question 1
Which of the following type definitions are examples of Applicative?
- ✔
Maybe - ✗
String - ✔
(->) afor anya - ✗
(,) afor anya - ✔
[ ] - ✔
Gen
As usual, String is not a type constructor, so cannot be an Applicative. Furthermore (,) a
does not admit a law-abiding applicative instance either, as we cannot implement pure:
instance Applicative ((,) x) where pure :: a -> (x, a) pure a = (??? , a)
Of the other options, Gen, Maybe, (->) a and [ ]. The latter three can be implemented as follows:
instance Applicative Maybe where pure x = Just x Just f <*> Just x = Just (f x) _ <*> _ = Nothing instance Applicative ((->) x) where pure a = \x -> a (xf <*> xa) x = xf x (xa x) instance Applicative [ ] where pure a = [ a ] [] <*> ys = [] (f:fs) <*> xs = map f xs ++ (fs <*> xs)
1.2 Question 2
Consider the following data type definition for a non-empty list in Haskell.
data NonEmptyList a = One a | Cons a (NonEmptyList a)
Which of the following are law-abiding Applicative instances for NonEmptyList?
- ✔
instance Applicative NonEmptyList where pure x = Cons x (pure x) (One f) <*> (One x) = One (f x) (One f) <*> (Cons x _) = One (f x) (Cons f _) <*> (One x) = One (f x) (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
- ✗
instance Applicative NonEmptyList where pure x = One x (One f) <*> (One x) = One (f x) (One f) <*> (Cons x _) = One (f x) (Cons f _) <*> (One x) = One (f x) (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
- ✔
instance Applicative NonEmptyList where pure x = One x One f <*> xs = fmap f xs (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs) where append (One x) ys = Cons x ys append (Cons x xs) ys = Cons x (xs `append` ys)
- ✗
instance Applicative NonEmptyList where pure x = Cons x (pure x) One f <*> xs = fmap f xs (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs) where append (One x) ys = Cons x ys append (Cons x xs) ys = Cons x (xs `append` ys)
Option 3 is analogous to the same Applicative instance we knows from regular lists, so is a valid Applicative instance.
Options 2 and 4 don't obey the first Applicative law, pure id <*> v = v.
Option 1 is also a valid applicative instance, analogous to the ZipList instance available in the Haskell standard library.
1.3 Question 3
Suppose I wanted to write a function pair, which takes two Applicative data structures and combines them in a tuple, of type:
pair :: (Applicative f) => f a -> f b -> f (a, b)
Select all correct implementations of pair.
- ✗
pair fa fb = pure fa <*> pure fb
- ✗
pair fa fb = pure (,) <*> pure fa <*> pure fb
- ✔
pair fa fb = pure (,) <*> fa <*> fb
- ✔
pair fa fb = fmap (,) fa <*> fb
- ✗There is no way to implement this function.
Options 1 and 2 are not type correct.
Options 3 and 4 are both correct, and equivalent, as fmap f x = (pure f <*> x), if the Applicative and Functor instances
are law-abiding.
2 Monads
2.1 Question 4
Which of the following type definitions are examples of Monad, or admit
law-abiding Monad instances?
- ✔
Maybe - ✗
String - ✔
(->) afor anya - ✗
(,) afor anya - ✔
[ ] - ✔
Gen
Monad instances can be written for Maybe, (->) a and [ ], and Gen is an
abstract type that also implements Monad. The rest have:
instance Monad Maybe where Just x >>= f = f x Nothing >>= f = Nothing instance Monad ((->) x) where (xa >>= axb) = \x -> axb (xa x) x instance Monad [ ] where xs >>= each = concatMap each xs
2.2 Question 5
We wish to write a function s of type [m a] -> m [a], for any monad m.
It will unpack each given value of type m a and collect their results into a list.
Which of the following is a correct implementation
of this function?
- ✗
s :: Monad m => [m a] -> m [a] s [] = [] s (a:as) = do x <- a xs <- s as return (x : xs)
- ✗
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do x <- a xs <- as return (x : xs)
- ✔
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do x <- a xs <- s as return (x : xs)
- ✗
s :: Monad m => [m a] -> m [a] s [] = return [] s (a:as) = do a s as return (a : as)
Only answer 3 is type correct.
3 Functor, Monad, Applicative
3.1 Question 6
Which of the following are correct definitions of the usual <*> operation
for the type [a]?
- ✔
fs <*> xs = do f <- fs x <- xs return (f x)
- ✔
fs <*> xs = [f x | f <- fs, x <- xs]
- ✔
fs <*> xs = fs >>= \f -> xs >>= \x -> [f x]
- ✗
fs <*> xs = fs >>= \f -> xs >>= \x -> f x
Answers 1, 2 and 3 are three different syntactic forms expressing the same operation. However, Answer 4 is not type correct.
3.2 Question 7
Recall that two functions are considered equal if they have the same output for every input. Select all the true statements below.
- ✗Kleisli composition is not well-defined in the
Maybemonad. - ✗Every monad
mand every functionf :: a -> m asatisfies the equalityf <=< f = f. - ✗Every instance of
Applicativehas to be an instance ofMonadas well. - ✔Every instance of
Monadhas to be an instance ofFunctoras well.
Answer 4 is correct. Answer 1 is not, since Kleisli composition is well-defined in every monad.
Answer 2 is not correct (think about e.g. the list monad). Answer 3 is not correct
either: while every Applicative has to be a Functor, they need not be instances of Monad.
3.3 Question 8
If a given unary type m is both a Monad instance and a Functor instance, which of the following would
definitely be correct implementations of fmap :: (a -> b) -> m a -> m b?
- ✗
fmap f [] = [] fmap f (x:xs) = map f xs
- ✔
fmap f = (<$>) f
- ✗
fmap f xs = f <*> xs
- ✔
fmap f xs = xs >>= \x -> return (f x)
The implementations 1 and 3 do not (or may not) type-check. The others are correct.